package com.baiyu.common.utils.file;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import ch.qos.logback.core.util.FileUtil;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.baiyu.common.config.BaiYuConfig;
import com.baiyu.common.constant.Constants;
import com.baiyu.common.utils.DateUtils;
import com.baiyu.common.utils.StringUtils;
import com.baiyu.common.utils.uuid.IdUtils;

/**
 * 文件处理工具类
 * 
 * @author baiyu
 */
public class FileUtils
{
    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";

    /**
     * 输出指定文件的byte数组
     * 
     * @param filePath 文件路径
     * @param os 输出流
     * @return
     */
    public static void writeBytes(String filePath, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            File file = new File(filePath);
            if (!file.exists())
            {
                throw new FileNotFoundException(filePath);
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            IOUtils.close(os);
            IOUtils.close(fis);
        }
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeImportBytes(byte[] data) throws IOException
    {
        return writeBytes(data, BaiYuConfig.getImportPath());
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @param uploadDir 目标文件
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeBytes(byte[] data, String uploadDir) throws IOException
    {
        FileOutputStream fos = null;
        String pathName = "";
        try
        {
            String extension = getFileExtendName(data);
            pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
            File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
            fos = new FileOutputStream(file);
            fos.write(data);
        }
        finally
        {
            IOUtils.close(fos);
        }
        return FileUploadUtils.getPathFileName(uploadDir, pathName);
    }

    /**
     * 移除路径中的请求前缀片段
     * 
     * @param filePath 文件路径
     * @return 移除后的文件路径
     */
    public static String stripPrefix(String filePath)
    {
        return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
    }

    /**
     * 删除文件
     * 
     * @param filePath 文件
     * @return
     */
    public static boolean deleteFile(String filePath)
    {
        boolean flag = false;
        File file = new File(filePath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            flag = file.delete();
        }
        return flag;
    }

    /**
     * 文件名称验证
     * 
     * @param filename 文件名称
     * @return true 正常 false 非法
     */
    public static boolean isValidFilename(String filename)
    {
        return filename.matches(FILENAME_PATTERN);
    }

    /**
     * 检查文件是否可下载
     * 
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(String resource)
    {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, ".."))
        {
            return false;
        }

        // 检查允许下载的文件规则
        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
        {
            return true;
        }

        // 不在允许下载的文件规则
        return false;
    }

    /**
     * 下载文件名重新编码
     * 
     * @param request 请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
    {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
        {
            // 火狐浏览器
            filename = new String(fileName.getBytes(), "ISO8859-1");
        }
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        else
        {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }

    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     * @return
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
    {
        String percentEncodedFileName = percentEncode(realFileName);

        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName);

        response.setHeader("Content-disposition", contentDispositionValue.toString());
    }

    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    public static String percentEncode(String s) throws UnsupportedEncodingException
    {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
        return encode.replaceAll("\\+", "%20");
    }

    /**
     * 获取图像后缀
     * 
     * @param photoByte 图像数据
     * @return 后缀名
     */
    public static String getFileExtendName(byte[] photoByte)
    {
        String strFileExtendName = "jpg";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
        {
            strFileExtendName = "gif";
        }
        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
        {
            strFileExtendName = "jpg";
        }
        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
        {
            strFileExtendName = "bmp";
        }
        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
        {
            strFileExtendName = "png";
        }
        return strFileExtendName;
    }

    /**
     * 获取文件名称 /profile/upload/2022/04/16/baiyu.png -- baiyu.png
     * 
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    public static String getName(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        int lastUnixPos = fileName.lastIndexOf('/');
        int lastWindowsPos = fileName.lastIndexOf('\\');
        int index = Math.max(lastUnixPos, lastWindowsPos);
        return fileName.substring(index + 1);
    }

    /**
     * 获取不带后缀文件名称 /profile/upload/2022/04/16/baiyu.png -- baiyu
     * 
     * @param fileName 路径名称
     * @return 没有文件路径和后缀的名称
     */
    public static String getNameNotSuffix(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        String baseName = FilenameUtils.getBaseName(fileName);
        return baseName;
    }


    /**
     * 判断指定的文件是否存在。
     *
     * @param fileName
     *            要判断的文件的文件名
     * @return 存在时返回true，否则返回false。
     *
     */
    public static boolean isFileExist(String fileName) {
        return new File(fileName).isFile();
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param file
     *            要创建的目录
     * @return 完全创建成功时返回true，否则返回false。
     *
     */
    public static boolean makeDirectory(File file) {
        if (file.isDirectory()) {
            return file.mkdirs();
        } else {
            File parent = file.getParentFile();
            if (parent != null) {
                return parent.mkdirs();
            }
        }
        return false;
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param fileName
     *            要创建的目录的目录名
     * @return 完全创建成功时返回true，否则返回false。
     *
     */
    public static boolean makeDirectory(String fileName) {
        File file = new File(fileName);
        return makeDirectory(file);
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directory
     *            要清空的目录
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false.
     *
     */
    public static boolean emptyDirectory(File directory) {
        @SuppressWarnings("unused")
        boolean result = false;
        File[] entries = directory.listFiles();
        for (int i = 0; i < entries.length; i++) {
            if (!entries[i].delete()) {
                result = false;
            }
        }
        return true;
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directoryName
     *            要清空的目录的目录名
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false。
     *
     */
    public static boolean emptyDirectory(String directoryName) {
        File dir = new File(directoryName);
        return emptyDirectory(dir);
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dirName
     *            要删除的目录的目录名
     * @return 删除成功时返回true，否则返回false。
     *
     */
    public static boolean deleteDirectory(String dirName) {
        return deleteDirectory(new File(dirName));
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dir
     *            要删除的目录
     * @return 删除成功时返回true，否则返回false。
     *
     */
    public static boolean deleteDirectory(File dir) {
        if ((dir == null) || !dir.isDirectory()) {
            throw new IllegalArgumentException("Argument " + dir + " is not a directory. ");
        }

        File[] entries = dir.listFiles();
        int sz = entries.length;

        for (int i = 0; i < sz; i++) {
            if (entries[i].isDirectory()) {
                if (!deleteDirectory(entries[i])) {
                    return false;
                }
            } else {
                if (!entries[i].delete()) {
                    return false;
                }
            }
        }

        if (!dir.delete()) {
            return false;
        }
        return true;
    }

    /**
     * 从文件路径得到文件名。
     *
     * @param filePath
     *            文件的路径，可以是相对路径也可以是绝对路径
     * @return 对应的文件名
     *
     */
    public static String getFileName(String filePath) {
        File file = new File(filePath);
        return file.getName();
    }

    /**
     * 从文件名得到文件绝对路径。
     *
     * @param fileName
     *            文件名
     * @return 对应的文件路径
     *
     */
    public static String getFilePath(String fileName) {
        File file = new File(fileName);
        return file.getAbsolutePath();
    }

    /**
     * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。
     * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，
     * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。
     *
     * @param filePath
     *            转换前的路径
     * @return 转换后的路径
     *
     */
    public static String toUNIXpath(String filePath) {
        return filePath.replace('\\', '/');
    }

    /**
     * 从文件名得到UNIX风格的文件绝对路径。
     *
     * @param fileName
     *            文件名
     * @return 对应的UNIX风格的文件路径
     *
     * @see #toUNIXpath(String filePath) toUNIXpath
     */
    public static String getUNIXfilePath(String fileName) {
        File file = new File(fileName);
        return toUNIXpath(file.getAbsolutePath());
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param fileName
     *            文件名
     * @return 文件名中的类型部分
     *
     */
    public static String getTypePart(String fileName) {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1) {
            return "";
        } else {
            return fileName.substring(point + 1, length);
        }
    }

    public static String getNameWithoutType(String fileName) {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1) {
            return "";
        } else {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param file
     *            文件
     * @return 文件名中的类型部分
     *
     */
    public static String getFileType(File file) {
        return getTypePart(file.getName());
    }

    /**
     * 得到文件的名字部分。 实际上就是路径中的最后一个路径分隔符后的部分。
     *
     * @param fileName
     *            文件名
     * @return 文件名中的名字部分
     *
     */
    public static String getNamePart(String fileName) {
        int point = getPathLastIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return fileName;
        } else if (point == length - 1) {
            int secondPoint = getPathLastIndex(fileName, point - 1);
            if (secondPoint == -1) {
                if (length == 1) {
                    return fileName;
                } else {
                    return fileName.substring(0, point);
                }
            } else {
                return fileName.substring(secondPoint + 1, point);
            }
        } else {
            return fileName.substring(point + 1);
        }
    }

    /**
     * 得到文件名中的父路径部分。 对两种路径分隔符都有效。 不存在时返回""。
     * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。
     *
     * @param fileName
     *            文件名
     * @return 父路径，不存在或者已经是父目录时返回""
     *
     */
    public static String getPathPart(String fileName) {
        int point = getPathLastIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return "";
        } else if (point == length - 1) {
            int secondPoint = getPathLastIndex(fileName, point - 1);
            if (secondPoint == -1) {
                return "";
            } else {
                return fileName.substring(0, secondPoint);
            }
        } else {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到路径分隔符在文件路径中首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName
     *            文件路径
     * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。
     *
     */
    public static int getPathIndex(String fileName) {
        int point = fileName.indexOf('/');
        if (point == -1) {
            point = fileName.indexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置后首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName
     *            文件路径
     * @param fromIndex
     *            开始查找的位置
     * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。
     *
     */
    public static int getPathIndex(String fileName, int fromIndex) {
        int point = fileName.indexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.indexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName
     *            文件路径
     * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。
     *
     */
    public static int getPathLastIndex(String fileName) {
        int point = fileName.lastIndexOf('/');
        if (point == -1) {
            point = fileName.lastIndexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置前最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName
     *            文件路径
     * @param fromIndex
     *            开始查找的位置
     * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。
     *
     */
    public static int getPathLastIndex(String fileName, int fromIndex) {
        int point = fileName.lastIndexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.lastIndexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 将文件名中的类型部分去掉。
     *
     * @param filename
     *            文件名
     * @return 去掉类型部分的结果
     *
     */
    public static String trimType(String filename) {
        int index = filename.lastIndexOf(".");
        if (index != -1) {
            return filename.substring(0, index);
        } else {
            return filename;
        }
    }

    /**
     * 得到相对路径。 文件名不是目录名的子节点时返回文件名。
     *
     * @param pathName
     *            目录名
     * @param fileName
     *            文件名
     * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名
     *
     */
    public static String getSubpath(String pathName, String fileName) {
        pathName = getUNIXfilePath(pathName).toLowerCase();
        fileName = getUNIXfilePath(fileName).toLowerCase();
        if (pathName.endsWith("/"))
            pathName = pathName.substring(0, pathName.length() - 1);
        int index = fileName.indexOf(pathName);
        if (index != -1) {
            return fileName.substring(index + pathName.length());
        } else {
            return fileName;
        }
    }

//	public static String getFileUri(File file) {
//		String uri = getSubpath(Constants.CMS_ROOT_DIR, file.toString());
//		uri = uri.replace("\\", "/");
//		return uri;
//	}

    public static String toPath(String path, String suffix) {
        if (!path.endsWith(suffix))
            path += suffix;
        return path;
    }

    public static File getFile(String fileid, String root) {
        String filename = toPath(root, "/") + fileid.replace("$", "/");
        return new File(filename);
    }

    /**
     * 根据文件id得到文件
     *
     * @param fileid
     * @return
     */
//	public static File getFile(String fileid) {
//		String filename = toPath(Constants.CMS_ROOT_DIR, "/") + fileid.replace("$", "/");
//		return new File(filename);
//	}

//	public static String getFileId(String absolutefilename) {
//		File file = new File(absolutefilename);
//		return getFileId(file);
//
//	}

    /**
     * 根据文件得到文件的id
     *
     * @param file
     * @return
     */
//	public static String getFileId(File file) {
//		String fileid = FileUtil.getSubpath(Constants.FILEPATH, file.toString());
//		fileid = FileUtil.getSubpath(Constants.CMS_ROOT_DIR, file.toString());
//		fileid = fileid.replace("/", "$");
//		return fileid;
//	}

    /**
     * 复制目录
     *
     * @param src_path
     * @param dest_path
     */
    public static void copyDir(File src_path, File dest_path) {
        File[] files = src_path.listFiles();
        for (int i = 0; i < files.length; i++) {
            File src = files[i];
            File dest = new File(dest_path.getPath() + File.separator + files[i].getName());
            if (!src.isDirectory())
                copyFile(src, dest);
        }
    }

    /**
     * 复制文件
     *
     * @param src
     * @param dest
     */
    public static void copyFile(File src, File dest) {
        try {
            File fPath = new File(dest.getParent());
            if (!fPath.exists()) {
                fPath.mkdirs();
            }
            System.out.println(src.getAbsoluteFile() + "  ->  " + dest.getAbsoluteFile());
            FileInputStream in = new FileInputStream(src);
            FileOutputStream out = new FileOutputStream(dest);
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = in.read(buffer)) != -1) {
                out.write(buffer, 0, length);
            }
            out.flush();
            out.close();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 目录过滤器
     */
    public static FilenameFilter Dirfilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            File file = new File(dir, name);
            return file.isDirectory();
        }
    };

    /**
     * 文件过滤器
     */
    public static FilenameFilter Filefilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            File file = new File(dir, name);
            return file.isFile();
        }
    };

    /**
     * 删除文件夹下所有内容，包括此文件夹
     */
    public static void delAll(File f) throws Exception {
        if (!f.exists())// 文件夹不存在不存在
        {
            throw new Exception("指定目录不存在:" + f.getName());
        }

        boolean rslt = true;
        if (!(rslt = f.delete())) {// 先尝试直接删除
            // 若文件夹非空。枚举、递归删除里面内容
            File subs[] = f.listFiles();
            for (int i = 0; i <= subs.length - 1; i++) {
                if (subs[i].isDirectory()) {
                    delAll(subs[i]);// 递归删除子文件夹内容
                }
                rslt = subs[i].delete();// 删除子文件夹本身
            }
            rslt = f.delete();// 删除此文件夹本身
        }

        if (!rslt) {
            throw new Exception("无法删除:" + f.getName());
        }
        return;
    }


    //	读取文件内容
    public static String readFileContent(File file) {
        try {
            if (!file.exists()) {

                FileUtils.makeDirectory(file.getParentFile());
                file.createNewFile();
            }
            FileReader reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            String s1 = null;
            String contents = "";
            while ((s1 = br.readLine()) != null) {
                contents = contents + s1 + "\r\n";
            }

            br.close();
            reader.close();
            return contents;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 更换扩展名
     *
     * @param file
     * @return
     */
    public static File changeExt(File file, String newExt) {
        String fname = "";
        if (file.getParent() != null) {
            fname = file.getParent() + File.separator + trimType(file.getName()) + "." + newExt;
        } else {
            fname = trimType(file.getName()) + "." + newExt;
        }
        return new File(fname);
    }

//	public static void main(String[] args) {
//		// String subpath = getSubpath("c:\\wwww\\",
//		// "c:\\wwww\\aa\\bb\\cc.exe");
//		/*
//		 * System.out.println(subpath); File file = getFile(subpath);
//		 * System.out.println(file); System.out.println(file.getParent());
//		 */
    ////		File file = getFile("$site$public$search");
    ////		File file2 = changeExt(file, "xx");
    ////		System.out.println(file2);
//	}

    public static void saveFileContent(File file, String content) throws IOException {
        if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
        BufferedWriter out;
        out = new BufferedWriter(new FileWriter(file));
        out.write(content);
        out.close();
    }

    public static String getFilePath(String relaPath, String name) {
        return FileUtil.class.getResource("/").getPath()  + relaPath  + File.separator + name;
    }


    public static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
